﻿namespace Summoner
{
    using UnityEngine;
    using System.Net;
    using System.IO;
    using System.Collections;
    using System;

    public class PartialLoad
    {
        private bool _isRunning = false;
        private string _assetName;
        private string _remoteFile;
        private string _localFile;
        private DateTime _localModifyDate;
        private DateTime _remoteModifyDate;
        private long _remoteFileSize = 0;
        private bool _hadError = false;
#if UNITY_WEBPLAYER || UNITY_WEBGL || UNITY_IOS
	    private bool _isDone = false;
#else
        private HttpWebResponse _AsynchResponse = null;
#endif
        public Action onLoadCompleted = null;

        /// <summary>
        /// Load asset to documents folder with asset name and extension name
        /// </summary>
        /// <param name="name"></param>
        public PartialLoad(string remoteFile, string localFile, Action callback)
        {
            this._assetName = this.GetAssetName(localFile);
            this._remoteFile = remoteFile;
            this._localFile = localFile;
            this.onLoadCompleted = callback;
            Debug.Log("[ResumableDownload] Creating download for: " + this._remoteFile);
            if(File.Exists(this._localFile))
            {
                this._localModifyDate = File.GetLastWriteTime(this._localFile);
            }
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this._remoteFile);
            // Only the header info, not full file!
            request.Method = "HEAD"; 
            // Get response will throw WebException if file is not found
            HttpWebResponse resp = null;
            try
            {
                resp = (HttpWebResponse)request.GetResponse();
            }
            catch (System.Exception e)
            {
                Debug.LogWarning("<color=red>ERROR: " + this._remoteFile + "</color>");
                Debug.LogWarning("ERROR: " + e);
                this._hadError = true;
                return;
            }
            this._remoteFileSize = resp.ContentLength;
            this._remoteModifyDate = resp.LastModified;
            resp.Close();
        }

        private string GetAssetName(string path)
        {
            path.Replace("\\", "/");
            string[] arr = path.Split('/');
            return arr[arr.Length - 1].Split('.')[0];
        }

        /// <summary>
        /// check file is outdate or not
        /// </summary>
        private bool IsOutdated
        {
            get
            {
                if(File.Exists(this._localFile))
                {
                    return this._remoteModifyDate > this._localModifyDate;
                }
                return true;
            }
        }

        // It's the callers responsibility to start this as a coroutine!
        public IEnumerator Download()
        {
            if (this._hadError) { yield break; }
            while (this._isRunning) { yield return null; }
            this._isRunning = true;
            long localFileSize = (File.Exists(_localFile)) ? (new FileInfo(_localFile)).Length : 0;
            if (localFileSize == this._remoteFileSize && !IsOutdated)
            {
                Debug.Log("File already cacled, not downloading: " + this._localFile);
                this._isRunning = false;
                if (this.onLoadCompleted != null)
                {
                    this.onLoadCompleted();
                }
                yield break; // We already have the file, early out
            }
            else if (localFileSize > this._remoteFileSize || this.IsOutdated)
            {
                try
                {
                    if (File.Exists(this._localFile))
                    {
                        File.Delete(this._localFile);
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogWarning("<color=red>Could not delete local file</color>");
                    Debug.LogError(e);
                }
                while (File.Exists(this._localFile)) { yield return null; }
                localFileSize = 0;
            }

#if UNITY_WEBPLAYER || UNITY_WEBGL || UNITY_IOS
		    using (WebClient client = new WebClient()) 
            {
			    client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
			    client.DownloadFileAsync(new Uri(this._remoteFile), this._localFile);
		    }
		    while (!this._isDone){ yield return null; }			
#else
            int bufferSize = 1024 * 1000;
            Debug.Log("Downloading: " + _remoteFile);
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_remoteFile);
            request.Timeout = 30000;
            request.AddRange((int)localFileSize, (int)_remoteFileSize - 1);
            request.Method = WebRequestMethods.Http.Post;
            request.BeginGetResponse(AsynchCallback, request);
            while (this._AsynchResponse == null) { yield return null; }

            Stream inStream = this._AsynchResponse.GetResponseStream();
            if(!Directory.Exists(AssetPath.DocumentsPath))
            {
                Directory.CreateDirectory(AssetPath.DocumentsPath);
            }
            FileStream outStream = new FileStream(_localFile, (localFileSize > 0) ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            int count = 0;
            byte[] buff = new byte[bufferSize];
            while ((count = inStream.Read(buff, 0, bufferSize)) > 0)
            {
                outStream.Write(buff, 0, count);
                outStream.Flush();
                yield return null;
            }
            outStream.Flush();
            outStream.Close();
            inStream.Close();
            request.Abort();
            this._AsynchResponse.Close();
            this._AsynchResponse = null;

            localFileSize = (File.Exists(_localFile)) ? (new FileInfo(_localFile)).Length : 0;
            while (localFileSize != _remoteFileSize)
            {
                localFileSize = (File.Exists(_localFile)) ? (new FileInfo(_localFile)).Length : 0;
                yield return null;
            }
#endif
            _isRunning = false;
            if(this.onLoadCompleted != null)
            {
                this.onLoadCompleted();
            }
        }

#if UNITY_WEBPLAYER || UNITY_WEBGL || UNITY_IOS
	    private void DownloadCompleted(System.Object sender, AsyncCompletedEventArgs e) 
        {
		    this._isDone = true;
	    }
#else
        // Throwind an exception here will not propogate to unity!
        private void AsynchCallback(IAsyncResult result)
        {
            if (result == null) { Debug.LogError("Asynch result is null!"); }
            HttpWebRequest webRequest = (HttpWebRequest)result.AsyncState;
            if (webRequest == null) { Debug.LogError("Could not cast to web request"); }
            this._AsynchResponse = webRequest.EndGetResponse(result) as HttpWebResponse;
            if (this._AsynchResponse == null) { Debug.LogError("Asynch response is null!"); }
            Debug.Log("[ResumableDownload] Download compleate");
        }
#endif

        public string AssetName { get { return this._assetName; } }
    }
}